﻿/// <reference path="_namespace.js" />
/// <reference path="../../../components/Container.js"/>
/// <reference path="../../../types/Float.js"/>
/// <reference path="../../../math/Vector3.js"/>
/// <reference path="../../../math/Matrix.js"/>

/*****************************************************************************
Ajaxna library class definition. Inheriting from ajax.geometry.shapes.Rectangle.
Author: krobinson 11/3/2008 6:10:27 PM.
Class:  ajax.xna.framework.graphics.Viewport
*****************************************************************************/
$addNamespace("ajax.xna.framework.graphics");
$imports("ajax.types.Float");
$imports("ajax.math.Vector3");
//-----------------------------------------------------------------------------

ajax.xna.framework.graphics.Viewport = function(doc, parentNode, layerName, x, y, width, height, graphicsScale)
{///<summary>Creates a new instance of the Viewport.</summary>
    ///<param name="graphicsScale" type="float">Not yet supported.</param>
    ajax.xna.framework.graphics.Viewport.superConstructor.call(this, doc, parentNode, layerName, width, height);

    // Always set clipping on our surface..
    this.layerStyle.overflow = "hidden";
    this.layerStyle.clip = "rect(0px " + (this.width) + "px " + (this.height) + "px 0px)"; // Doesn't seem to help...

    this.setPosition(x, y);
}
$extend("ajax.xna.framework.graphics.Viewport", "ajax.components.Container");
if ($getDesignMode())
    ajax.xna.framework.graphics.Viewport.prototype = ajax.components.Container.prototype;
//-----------------------------------------------------------------------------

// Private members
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.prototype._minZ = 0;
ajax.xna.framework.graphics.Viewport.prototype._maxZ = 1;
ajax.xna.framework.graphics.Viewport.prototype._scale = 1;


// Public Accessors
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.prototype.get_scale = function()
{///<value type="float">Gets or sets the scale. This is NOT currently supported.</value>
    return this._scale;
}
ajax.xna.framework.graphics.Viewport.prototype.set_scale = function(value) { this._scale = value; }

ajax.xna.framework.graphics.Viewport.prototype.get_minDepth = function()
{///<value type="float"/>    
    return this._minZ;
}
ajax.xna.framework.graphics.Viewport.prototype.set_minDepth = function(value) { this._minZ = value; }
ajax.xna.framework.graphics.Viewport.prototype.get_maxDepth = function()
{///<value type="float"/>
    return this._maxZ;
}
ajax.xna.framework.graphics.Viewport.prototype.set_maxDepth = function(value) { this._maxZ = value; }


// Private Methods
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.prototype._withinEpsilon = function(a, b)
{
    var num = a - b;
    return ((-ajax.types.Float.epsilon <= num) && (num <= ajax.types.Float.epsilon));
}

// Public Methods
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.prototype.projectTransformedVertexBuffer = function(unTransVB, transVB, matrix)
{///<summary>Projects an array of vertices which are already transformed by the World*View*Projection Matrix.</summary>
    ///<param name="unTransVB" type="Array" elementType="ajax.math.Vector3">The untransformed Vertex Buffer.</param>
    ///<param name="transVB" type="Array" elementType="ajax.math.Vector3">The vertex buffer transformed by the W*V*P matrix.</param>
    ///<param name="matrix" type="ajax.math.Matrix"/>The World*View*Projection matrix used to transform.</param>
    ///<returns type="Array" elementType="ajax.math.Vector3"/>
    var arr = new Array();
    matrix = ajax.math.Matrix.cast(matrix);
    var source;
    var vector;
    for (var i = 0; i < transVB.length; i++)
    {
        source = ajax.math.Vector3.cast(unTransVB[i]);
        vector = ajax.math.Vector3.createFrom(transVB[i]); // Create a copy of the transformed vert

        var a = (((source.x * matrix.m[0][3]) + (source.y * matrix.m[1][3])) + (source.z * matrix.m[2][3])) + matrix.m[3][3];
        if (!this._withinEpsilon(a, 1))
            vector.divideScalar(a);

        vector.x = (((vector.x + 1) * 0.5) * this.width) + this.x;
        vector.y = (((-vector.y + 1) * 0.5) * this.height) + this.y;
        vector.z = (vector.z * (this._maxZ - this._minZ)) + this._minZ;
        arr[i] = vector;
    }
    return arr;
}

ajax.xna.framework.graphics.Viewport.prototype.project = function(source, matrix)
{///<summary>Projects a 3D vector from object space into screen space.</summary>
    ////<param name="source" type="ajax.math.Vector3">The source vector to project to screen coords</param>
    ///<param name="matrix" type="ajax.math.Matrix">The World*View*Projection Matrix</param>
    ///<returns type="ajax.math.Vector3"/>
    matrix = ajax.math.Matrix.cast(matrix);
    var vector = matrix.transformVertex(source); // Creates a copy

    var a = (((source.x * matrix.m[0][3]) + (source.y * matrix.m[1][3])) + (source.z * matrix.m[2][3])) + matrix.m[3][3];
    if (!this._withinEpsilon(a, 1))
        vector.divideScalar(a);

    vector.x = (((vector.x + 1) * 0.5) * this.width) + this.x;
    vector.y = (((-vector.y + 1) * 0.5) * this.height) + this.y;
    vector.z = (vector.z * (this._maxZ - this._minZ)) + this._minZ;

    return vector;
}
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.prototype.projectArray = function(vb, matrix)
{///<summary>Will project an entire vertex buffer and return an array of Vector3s containing the transformed positions.</summary>
    ///<param name="vb" type="Array" elementType="ajax.math.Vector3">The Vertex Buffer to project.</param>
    ///<param name="matrix" type="ajax.math.Matrix">The World*View*Projection Matrix</param>  
    ///<returns type="Array" elementType"              
    var arr = new Array(vb.length);
    for (var i = 0; i < vb.length; i++)
        arr[i] = this.project(vb[i], matrix);
    return arr;
}

ajax.xna.framework.graphics.Viewport.prototype.unProject = function(source, invMatWorldViewProjection)
{///<param name="source" type="ajax.math.Vector3">The source vector</param>
    ///<param name="invMatWorldViewProjection" type="ajax.math.Matrix">The Inverse of the World*View*Projection Matrix</param>
    var position = new ajax.math.Vector3();
    position.x = (((source.x - this.x) / parseFloat(this.width)) * 2) - 1;
    position.y = -((((source.y - this.y) / parseFloat(this.height)) * 2) - 1);
    position.z = (source.z - this._minZ) / (this._maxZ - this._minZ);
    position = Vector3.transformCoordinate(position, matrix);
    var a = (((source.x * matrix.m[0][3]) + (source.y * matrix.m[1][3])) + (source.z * matrix.m[2][3])) + matrix.m[3][3];
    if (!withinEpsilon(a, 1))
    {
        position.divideScalar(a);
    }
    return position;
}

ajax.xna.framework.graphics.Viewport.prototype.get_aspectRatio = function()
{///<returns type="float"/>
    if ((this.height != 0) && (this.width != 0))
    {
        return (parseFloat(this.width) / parseFloat(this.height));
    }
    return 0;
}



ajax.xna.framework.graphics.Viewport.prototype.toString = function()
{
    return String.toFormat("X:{0} Y:{1} Width:{2} Height:{3} MinDepth:{4} MaxDepth:{5}",
        this.x, this.y, this.width, this.height, this.get_minDepth(), this.get_maxDepth()
    );
}

// Statics
//-----------------------------------------------------------------------------
ajax.xna.framework.graphics.Viewport.cast = function(obj)
{///<returns type="ajax.xna.framework.graphics.Viewport"/>
    return obj;
}
